
//  Copyright (C) 2009-2012 Luca Piccioni
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;

namespace Derm.Render
{
	/// <summary>
	/// Projection matrix.
	/// </summary>
	public class ProjectionMatrix : Matrix4x4
	{
		#region Constructors

		/// <summary>
		/// ProjectionMatrix constructor.
		/// </summary>
		/// <remarks>
		/// It set this ModelMatrix to identity.
		/// </remarks>
		public ProjectionMatrix()
		{
			SetIdentity();
		}

		/// <summary>
		/// ProjectionMatrix copy constructor.
		/// </summary>
		/// <param name="m">
		/// A <see cref="ProjectionMatrix"/> to be copied.
		/// </param>
		public ProjectionMatrix(ProjectionMatrix m) : base(m)
		{

		}

		#endregion

		#region Projections

		/// <summary>
		/// Set frustrum projection matrix.
		/// </summary>
		/// <param name="left">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="right">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="bottom">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="top">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="near">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="far">
		/// A <see cref="System.Single"/>
		/// </param>
		public void SetFustrum(float left, float right, float bottom, float top, float near, float far)
		{
			// Column 1
			this[0,0] = 2.0f*near / (right-left);
			this[0,1] = 0.0f;
			this[0,2] = 0.0f;
			this[0,3] = 0.0f;
			// Column 2
			this[1,0] = 0.0f;
			this[1,1] = 2.0f*near / (top-bottom);
			this[1,2] = 0.0f;
			this[1,3] = 0.0f;
			// Column 3
			this[2,0] = (right+left) / (right-left);
			this[2,1] = (top+bottom) / (top-bottom);
			this[2,2] = (-far-near) / (far-near);
			this[2,3] = -1.0f;
			// Coumn 4
			this[3,0] = 0.0f;
			this[3,1] = 0.0f;
			this[3,2] = -2.0f*far*near / (far-near);
			this[3,3] = 0.0f;
		}

		/// <summary>
		/// Set a perspective projection matrix.
		/// </summary>
		public void SetPerspective(float fovy, float aspectRatio, float near, float far)
		{
			float ymax = near * (float) Math.Tan((double) fovy * Math.PI / 360.0);
			float xmax = ymax * aspectRatio;

			SetFustrum(-xmax, +xmax, -ymax, +ymax, near, far);
		}

		/// <summary>
		/// Set orthographic projection matrix.
		/// </summary>
		/// <param name="left">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="right">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="bottom">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="top">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="near">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="far">
		/// A <see cref="System.Single"/>
		/// </param>
		public void SetOrtho(float left, float right, float bottom, float top, float near, float far)
		{
			// Column 1
			this[0,0] = 2.0f / (right-left);
			this[0,1] = 0.0f;
			this[0,2] = 0.0f;
			this[0,3] = 0.0f;
			// Column 2
			this[1,0] = 0.0f;
			this[1,1] = 2.0f / (top-bottom);
			this[1,2] = 0.0f;
			this[1,3] = 0.0f;
			// Column 3
			this[2,0] = 0.0f;
			this[2,1] = 0.0f;
			this[2,2] = -2.0f / (far - near);
			this[2,3] = 0.0f;
			// Column 4
			this[3,0] = -(right + left) / (right - left);
			this[3,1] = -(top + bottom) / (top - bottom);
			this[3,2] = -(far + near) / (far - near);
			this[3,3] = 1.0f;
		}

		/// <summary>
		/// Set bidimensional orthographic projection matrix.
		/// </summary>
		/// <param name="left">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="right">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="bottom">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="top">
		/// A <see cref="System.Single"/>
		/// </param>
		public void SetOrtho2D(float left, float right, float bottom, float top)
		{
			SetOrtho(left, right, bottom, top, -1.0f, +1.0f);
		}

		#endregion

		#region ICloneable Implementation

		/// <summary>
		/// Clone this ProjectionMatrix.
		/// </summary>
		/// <returns>
		/// It returns a deep copy of this ProjectionMatrix.
		/// </returns>
		public override Matrix Clone()
		{
			return (new ProjectionMatrix(this));
		}

		#endregion
	}
}
